package cn.bossfriday.im.common.db;

import cn.bossfriday.im.common.conf.ConfigurationAllLoader;
import cn.bossfriday.im.common.entity.conf.DataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.lang.StringUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;

/**
 * DbUtilExecutor: 对dbutils的简单封装；
 *
 * @author chenx
 */
public class DbUtilExecutor {
    private static HikariConfig hikariConfig;
    private static HikariDataSource dataSource;

    static {
        DataSource sourceConfig = ConfigurationAllLoader.getInstance().getGlobalConfig().getDatasource();

        hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(sourceConfig.getUrl());
        hikariConfig.setUsername(sourceConfig.getUserName());
        hikariConfig.setPassword(sourceConfig.getPassword());
        hikariConfig.setDriverClassName(sourceConfig.getDriver());
        hikariConfig.setMaximumPoolSize(sourceConfig.getMaximumPoolSize());
        hikariConfig.setMinimumIdle(sourceConfig.getMinimumIdle());
        hikariConfig.setIdleTimeout(sourceConfig.getIdleTimeout());
        hikariConfig.setConnectionTimeout(sourceConfig.getConnectionTimeout());
        hikariConfig.setLeakDetectionThreshold(sourceConfig.getLeakDetectionThreshold());

        dataSource = new HikariDataSource(hikariConfig);
    }

    private DbUtilExecutor() {
        // do nothing
    }

    /**
     * getDataSource
     */
    public static HikariDataSource getDataSource() {
        return dataSource;
    }

    /**
     * checkConnection
     *
     * @return
     * @throws SQLException
     */
    public static boolean checkConnection() throws SQLException {
        String testSql = "SELECT NOW()";
        try (Connection connection = getConnection()) {
            QueryRunner queryRunner = new QueryRunner();
            return queryRunner.query(connection, testSql, rs -> {
                if (rs.next()) {
                    String currentTime = rs.getString(1);

                    return StringUtils.isNotEmpty(currentTime);
                }

                return false;
            });
        }
    }

    /**
     * executeUpdate
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static int executeUpdate(String sql, Object... params) throws SQLException {
        try (Connection connection = getConnection()) {
            QueryRunner queryRunner = new QueryRunner();

            return queryRunner.update(connection, sql, params);
        }
    }

    /**
     * queryObject
     *
     * @param sql
     * @param type
     * @param params
     * @param <T>
     * @return
     * @throws SQLException
     */
    public static <T> T queryObject(String sql, Class<T> type, Object... params) throws SQLException {
        try (Connection connection = getConnection()) {
            QueryRunner queryRunner = new QueryRunner();

            return queryRunner.query(connection, sql, new BeanHandler<>(type), params);
        }
    }

    /**
     * queryList
     *
     * @param sql
     * @param type
     * @param params
     * @param <T>
     * @return
     * @throws SQLException
     */
    public static <T> List<T> queryList(String sql, Class<T> type, Object... params) throws SQLException {
        try (Connection connection = getConnection()) {
            QueryRunner queryRunner = new QueryRunner();

            return queryRunner.query(connection, sql, new BeanListHandler<>(type), params);
        }
    }

    /**
     * now
     *
     * @return
     */
    public static Timestamp now() {
        return Timestamp.from(Instant.now());
    }

    /**
     * close
     */
    public static void close() {
        if (dataSource != null) {
            dataSource.close();
        }
    }

    /**
     * getConnection
     *
     * @return
     * @throws SQLException
     */
    private static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

